use std::{
    cell::OnceCell,
    error::Error,
    io::Write,
    ops::Deref,
    path::{Path, PathBuf},
    process::Command,
    sync::mpsc::{channel, Receiver, Sender},
    thread::{self, sleep},
    time::Duration,
};

use egui::{Color32, Margin, Vec2};

use native_dialog::{FileDialog, MessageDialog, MessageType};
use thunderdome::Index;

use QcTools::sync::Lazy;
use QcUI::component::{
    Button, ButtonMessage, Canvas, FlexDirection, Label, Panel, PanelWindow, TextBox, ToUi, Widget,
};

use crate::core::message::Page;

use crate::core::message::EditorMessage;

pub type ProjectList = Vec<(String, String)>;

static PROJECT_LIST_PATH: Lazy<PathBuf> =
    Lazy::new(|| Path::new(env!("appdata")).join("quincy/project_list.json"));

#[derive(Debug)]
enum ProjectHubMessage {
    OpenProject,
    CreateProject,
    OpenProjectByGo(String, String),
    DeleteProject(String),
}

pub struct ProjectHubPage {
    canvas: Canvas,
    sender: Sender<EditorMessage>,
    hub_sender: Sender<ProjectHubMessage>,
    hub_receiver: Receiver<ProjectHubMessage>,
    path_text_box: Option<Index>,
    project_list: ProjectList,
}

impl ProjectHubPage {
    pub fn new(sender: Sender<EditorMessage>) -> Self {
        let (hub_sender, hub_receiver) = channel::<ProjectHubMessage>();
        let mut hub = ProjectHubPage {
            canvas: Canvas::new(),
            sender,
            path_text_box: None,
            hub_sender,
            hub_receiver,
            project_list: Vec::new(),
        };

        hub.init_view();

        thread::spawn(|| {
            Self::check_environment();
        });
        hub
    }

    pub fn init_view(&mut self) {
        let sender = self.sender.clone();
        println!("{:?}", PROJECT_LIST_PATH.deref());
        let project_list_path = Path::new(PROJECT_LIST_PATH.deref());
        if project_list_path.exists() {
            let value = std::fs::read_to_string(project_list_path).unwrap();
            let mut project_list: ProjectList =
                serde_json::from_str(&value).expect("读取项目列表配置文件失败");
            self.project_list.append(&mut project_list);
        }

        let mut list = vec![];

        for project in &self.project_list {
            let item = Panel::new(Widget::default().with_margin(Margin {
                top: 20.,
                ..Default::default()
            }))
            .with_spacing(20.)
            .with_children(
                vec![
                    {
                        let name = project.0.clone();
                        self.canvas.add_child(
                            Label::new(Widget::default().with_width(600.))
                                .with_color(Color32::WHITE)
                                .with_text(&name)
                                .toUi(),
                        )
                    },
                    {
                        let button = Button::new(
                            Widget::default()
                                .with_background(Color32::from_rgb(179, 128, 0))
                                .with_height(20.)
                                .with_width(50.)
                                .on_event(ButtonMessage::Clicked.into(), {
                                    let hub_sender = self.hub_sender.clone();
                                    let name = project.0.clone();
                                    let path = project.1.clone();
                                    Box::new(move |_msg| {
                                        hub_sender
                                            .send(ProjectHubMessage::OpenProjectByGo(
                                                name.clone(),
                                                path.clone(),
                                            ))
                                            .unwrap();
                                    })
                                })
                                .build(&mut self.canvas),
                        )
                        .with_text("打开")
                        .toUi();
                        self.canvas.add_child(button)
                    },
                    {
                        let button = Button::new(
                            Widget::default()
                                .with_background(Color32::from_rgb(128, 0, 0))
                                .with_height(20.)
                                .with_width(50.)
                                .on_event(ButtonMessage::Clicked.into(), {
                                    let hub_sender = self.hub_sender.clone();
                                    let name = project.0.clone();
                                    Box::new(move |_msg| {
                                        hub_sender
                                            .send(ProjectHubMessage::DeleteProject(name.clone()))
                                            .unwrap();
                                    })
                                })
                                .build(&mut self.canvas),
                        )
                        .with_text("删除")
                        .toUi();
                        self.canvas.add_child(button)
                    },
                ],
                &mut self.canvas,
            );

            list.push(self.canvas.add_child(item.toUi()));
        }

        let open_project_button = {
            let hub_sender = self.hub_sender.clone();

            Button::new(
                Widget::default()
                    .with_background(Color32::from_rgb(179, 128, 0))
                    .with_height(30.)
                    .with_width(100.)
                    .on_event(
                        ButtonMessage::Clicked.into(),
                        Box::new(move |_msg| {
                            hub_sender.send(ProjectHubMessage::OpenProject).unwrap();
                        }),
                    )
                    .build(&mut self.canvas),
            )
            .with_text("打开项目")
            .toUi()
        };

        let create_project_button = {
            let hub_sender = self.hub_sender.clone();

            Button::new(
                Widget::default()
                    .with_background(Color32::from_rgb(0, 128, 0))
                    .with_height(30.)
                    .with_width(100.)
                    .on_event(
                        ButtonMessage::Clicked.into(),
                        Box::new(move |_msg| {
                            hub_sender.send(ProjectHubMessage::CreateProject).unwrap();
                        }),
                    )
                    .build(&mut self.canvas),
            )
            .with_text("新建项目")
            .toUi()
        };
        let button_panel = Panel::new(Widget::default().with_name("Panel").with_height(30.))
            .with_orientation(FlexDirection::Row)
            .with_spacing(20.)
            .with_children(
                vec![
                    { self.canvas.add_child(open_project_button) },
                    { self.canvas.add_child(create_project_button) },
                    {
                        let textbox = TextBox::new(
                            Widget::default()
                                .with_width(500.)
                                .with_height(30.)
                                .with_padding(5f32.into())
                                .with_background(Color32::WHITE),
                        )
                        .with_hint_text("输入项目路径")
                        .toUi();
                        let index = self.canvas.add_child(textbox);
                        self.path_text_box = Some(index);
                        index
                    },
                    {
                        let button = Button::new(
                            Widget::default()
                                .with_background(Color32::from_rgb(0, 128, 0))
                                .with_height(30.)
                                .with_width(30.)
                                .on_event(
                                    ButtonMessage::Clicked.into(),
                                    Box::new(move |msg| {
                                        println!("打开项目");
                                    }),
                                )
                                .build(&mut self.canvas),
                        )
                        .with_text("GO")
                        .toUi();
                        self.canvas.add_child(button)
                    },
                ],
                &mut self.canvas,
            );

        Panel::new(
            Widget::default()
                .with_padding(100f32.into())
                .with_background(Color32::from_rgb(23, 23, 26))
                .with_width(f32::INFINITY)
                .with_height(f32::INFINITY),
        )
        .with_orientation(FlexDirection::Column)
        .with_children(
            vec![{ self.canvas.add_child(button_panel.toUi()) }, {
                let panel = Panel::default()
                    .with_orientation(FlexDirection::Column)
                    .with_children(list, &mut self.canvas)
                    .toUi();
                self.canvas.add_child(panel)
            }],
            &mut self.canvas,
        )
        .build(&mut self.canvas);
    }

    pub fn check_environment() {
        fn is_npm_installed() -> bool {
            let output = Command::new("cmd")
                .arg("/C npm --version")
                .output()
                .unwrap();

            output.status.success()
        }

        fn is_pnpm_installed() -> bool {
            let output = Command::new("cmd")
                .arg("/C pnpm --version")
                .output()
                .unwrap();

            output.status.success()
        }

        fn is_tiged_installed() -> bool {
            let output = Command::new("cmd").arg("/C tiged --help").output().unwrap();
            output.status.success()
        }

        fn install_tiged() -> bool {
            let output = Command::new("cmd")
                .arg("/C npm install -g tiged")
                .output()
                .unwrap();
            output.status.success()
        }

        fn install_pnpm() -> bool {
            let output = Command::new("cmd")
                .arg("/C npm install -g pnpm")
                .output()
                .unwrap();
            output.status.success()
        }

        if !is_npm_installed() {
            let _ = MessageDialog::new()
                .set_title("环境检测")
                .set_text("npm未安装")
                .show_alert();
            std::process::exit(0);
        }

        if !is_tiged_installed() {
            let _ = MessageDialog::new()
                .set_title("环境检测")
                .set_text("tiged未安装,正在为你安装。")
                .show_alert();

            if !install_tiged() {
                let _ = MessageDialog::new()
                    .set_title("环境检测")
                    .set_text("tiged安装失败")
                    .show_alert();
                std::process::exit(0);
            }
        }

        if !is_pnpm_installed() {
            let _ = MessageDialog::new()
                .set_title("环境检测")
                .set_text("pnpm未安装,正在为你安装。")
                .show_alert();

            if !install_pnpm() {
                let _ = MessageDialog::new()
                    .set_title("环境检测")
                    .set_text("pnpm安装失败")
                    .show_alert();
                std::process::exit(0);
            }
        }
    }

    fn create_project(&self, path: &str) {
        let output = Command::new("cmd")
            .arg(&format!(
                "/C tiged https://github.com/996666925/quincy-template {}",
                path
            ))
            .output()
            .unwrap();
    }

    fn open_project(&self, path: &str) {
        let output = Command::new("cmd")
            .arg(&format!(
                "/C tiged https://github.com/996666925/quincy-template {}",
                path
            ))
            .output()
            .unwrap();
    }

    fn save_project_list(&mut self, name: &str, path: &PathBuf) {
        let path = path.clone().into_os_string().into_string().unwrap();
        let name = name.to_string();

        if self
            .project_list
            .iter()
            .find(|project| project.1 == path)
            .is_none()
        {
            self.project_list.push((name, path));

            let config = serde_json::to_string(&self.project_list).unwrap();
            let dir = PROJECT_LIST_PATH.parent().unwrap();
            if !dir.exists() {
                std::fs::create_dir_all(&*dir).unwrap();
            }
            std::fs::write(&*PROJECT_LIST_PATH, config).unwrap();
        }
    }

    fn delete_project_list(&mut self, name: &str) {
        let name = name.to_string();

        self.project_list = self
            .project_list
            .clone()
            .into_iter()
            .filter(|p| p.0 != name)
            .collect();

        let config = serde_json::to_string(&self.project_list).unwrap();
        let dir = PROJECT_LIST_PATH.parent().unwrap();
        if !dir.exists() {
            std::fs::create_dir_all(&*dir).unwrap();
        }
        std::fs::write(&*PROJECT_LIST_PATH, config).unwrap();

        self.canvas.clear();
        self.project_list.clear();
        self.init_view();
    }
}

impl PanelWindow for ProjectHubPage {
    fn get_canvas(&mut self) -> &mut Canvas {
        &mut self.canvas
    }

    fn get_size(&self) -> Vec2 {
        Vec2::new(1000., 580.)
    }

    fn update(&mut self) {
        while let Ok(msg) = self.hub_receiver.try_recv() {
            println!("msg:{:?}", msg);

            match msg {
                ProjectHubMessage::OpenProject => {
                    let result = FileDialog::new()
                        .set_title("打开项目")
                        .show_open_single_dir()
                        .unwrap();
                    if let Some(path) = result {
                        let project_name = path
                            .file_name()
                            .unwrap()
                            .to_os_string()
                            .into_string()
                            .unwrap();
                        self.save_project_list(&project_name, &path);
                        let sender = self.sender.clone();
                        sender
                            .send(EditorMessage::GoToEditor(path, project_name))
                            .unwrap();
                    }
                }
                ProjectHubMessage::CreateProject => {
                    let result = FileDialog::new()
                        .set_title("新建项目")
                        .show_open_single_dir()
                        .unwrap();

                    if let Some(path) = result {
                        let path_str = path.clone().into_os_string().into_string().unwrap();
                        self.create_project(&path_str);

                        let project_name = path
                            .file_name()
                            .unwrap()
                            .to_os_string()
                            .into_string()
                            .unwrap();
                        self.save_project_list(&project_name, &path);
                        let sender = self.sender.clone();
                        thread::spawn(move || {
                            sleep(Duration::from_secs(1));
                            sender
                                .send(EditorMessage::GoToEditor(path, project_name))
                                .unwrap();
                        });
                    }
                }
                ProjectHubMessage::OpenProjectByGo(name, path) => {
                    let sender = self.sender.clone();
                    sender
                        .send(EditorMessage::GoToEditor(
                            Path::new(&path).to_path_buf(),
                            name,
                        ))
                        .unwrap();
                }
                ProjectHubMessage::DeleteProject(name) => self.delete_project_list(&name),
            }
        }
    }
}

#[test]
fn test() {
    let output = Command::new("cmd")
        .arg("/C npm --version")
        .output()
        .unwrap();

    println!("{}", output.status.success())
}
